﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace Borg.Framework
{
    internal class SetCommand : ICommand
    {
        private readonly string[] _arguments = new[] { "Extension" };

        public IEnumerable<string> GetArguments(string[] command, int pos)
        {
            string[] strArray = null;
            int i;

            switch (pos)
            {
                case 0:
                    return _arguments;

                case 1:
                    if (command.Length >= 2)
                    {
                        if (command[1] == "Extension")
                        {
                            strArray = new string[Runtime.Repository.ExtensionInfos.Count];

                            for (i = 0; i < strArray.Length; i++)
                                strArray[i] = Runtime.Repository.ExtensionInfos[i].Name;
                        }

                        return strArray;
                    }

                    return new string[0];

                case 2:
                    if (command.Length >= 3 && command[2] != "Runtime")
                    {
                        var objects = GetObjects(command);

                        if (objects.Length == 0)
                            return new string[0];

                        var properties = objects[0].GetType().GetProperties();
                        strArray = new string[properties.Length];

                        for (i = 0; i < strArray.Length; i++)
                            strArray[i] = properties[i].Name;

                        return strArray;
                    }

                    return new string[0];
            }

            return null;
        }

        private static object[] GetObjects(IList<string> command)
        {
            string str;

            var elements =
                RuntimeCommands.Find(new[] { "ExtensionInfo", command[2] }, 0, out str);

            if (elements.Length == 0)
                return new object[0];

            var obs = new object[elements.Length];

            for (int i = 0; i < obs.Length; i++)
                obs[i] = ((ExtensionInfo) elements[i]).Object;

            return obs;
        }

        private string Set(string[] command)
        {
            if (command.Length < 2)
                return "Too few arguments.";

            if (!RuntimeCommands.ContainsArgument(command[1], _arguments))
                return string.Format("Invalid argument \"{0}\"", command[1]);

            var flag = false;

            if (command.Length < 3)
                return "Too few arguments";

            var obs = GetObjects(command);

            if (obs.Length == 0)
                return string.Format("{0} {1} not found", command[1], command[2]);

            if (command.Length == 3)
                flag = true;

            var builder = new StringBuilder();

            var type = obs[0] != null
                ? obs[0].GetType()
                : typeof(Runtime);
            
            if (flag)
            {
                foreach (var propInfo in type.GetProperties())
                    builder.AppendLine(propInfo.Name);

                return builder.ToString();
            }

            var strArray = command[3].Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
            var property = type.GetProperty(strArray[0]);

            if (property == null)
                return string.Format("Property \"{0}\" not found", command[3]);

            if (strArray.Length < 2)
                foreach (var obj in obs)
                    builder.AppendLine(property.GetValue(obj, null).ToString());
            else
                return property.CanWrite
                           ? SetValue(property, obs, strArray[1])
                           : string.Format("Property \"{0}\" is readonly.", property.Name);

            return builder.ToString();
        }

        internal static string SetValue(PropertyInfo pi, object[] objects, string str)
        {
            switch (pi.PropertyType.Name)
            {
                case "Boolean":
                    bool b;

                    if (bool.TryParse(str, out b))
                        foreach (var obj in objects)
                            pi.SetValue(obj, b ? 1 : 0, null);
                    break;
                
                case "String":
                    var s = str.Trim(new[] { '"' });

                    foreach (var obj in objects)
                        pi.SetValue(obj, s, null);
                    break;
                
                case "Int32":
                    int i32;

                    if (int.TryParse(str, out i32))
                        foreach (var obj in objects)
                            pi.SetValue(obj, i32, null);
                    break;
                
                case "Int64":
                    long i64;

                    if (long.TryParse(str, out i64))
                        foreach (var obj in objects)
                            pi.SetValue(obj, i64, null);
                    break;
                
                default:
                    return "Only Boolean, String, and Integer types are assignable";
            }

            return "Assigned";
        }

        public CommandHandler Handler
        {
            get { return Set; }
        }

        public string Help
        {
            get { return "Help for command \"set\""; }
        }

        public string Name
        {
            get { return "set"; }
        }
    }
}

